#include "adalog/LogFormatter.h"
#include <chrono>
#include <ctime>
#include <iomanip>
#include <mutex>
#include <sstream>

namespace adalog
{
    class LoggerNameItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        { 
            os << event->GetLoggerName();
        }
    };

    class LogLevelItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            os << LogLevel::ToString(event->GetLogLevel());
        }
    };

    class ThreadIdItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            os << event->GetThreadId();
        }
    };

    class PayloadItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            os << event->GetPayload();
        }
    };

    class YearItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            std::time_t time = std::chrono::system_clock::to_time_t(event->GetLogTime());
            std::tm* local_time = std::localtime(&time);
            int year = local_time->tm_year + 1900;
            os << year;
        }
    };

    class MonthItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            std::time_t time = std::chrono::system_clock::to_time_t(event->GetLogTime());
            std::tm* local_time = std::localtime(&time);
            auto month = static_cast<unsigned int>(local_time->tm_mon + 1); // 月份范围是0 - 11，需加1
            os << std::setw(2) << std::setfill('0') << month << std::setfill(' ');
        }
    };

    class DayItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            std::time_t time = std::chrono::system_clock::to_time_t(event->GetLogTime());
            std::tm* local_time = std::localtime(&time);
            auto day = static_cast<unsigned int>(local_time->tm_mday); // 月份范围是0 - 11，需加1
            os << std::setw(2) << std::setfill('0') << day << std::setfill(' ');
        }
    };

    class HourItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            std::time_t time = std::chrono::system_clock::to_time_t(event->GetLogTime());
            std::tm* local_time = std::localtime(&time);
            os << std::put_time(local_time, "%H");
        }
    };

    class MinuteItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            std::time_t time = std::chrono::system_clock::to_time_t(event->GetLogTime());
            std::tm* local_time = std::localtime(&time);
            os << std::put_time(local_time, "%M");
        }
    };

    class SecondItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            std::time_t time = std::chrono::system_clock::to_time_t(event->GetLogTime());
            std::tm* local_time = std::localtime(&time);
            os << std::put_time(local_time, "%S");
        }
    };

    class FileNameItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            os << event->GetFileName();
        }
    };

    class LineNumberItem : public LogFormatter::PatternItem
    {
    public:
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            os << event->GetLine();
        }
    };

    class OtherStringItem : public LogFormatter::PatternItem
    {
    public:
        OtherStringItem(const std::string& str) : str_(str) {}
        void Append(std::ostream& os, LogEvent::Ptr event) override
        {
            os << str_;
        }
    private:
        std::string str_;
    };

    LogFormatter::LogFormatter()
    {
        SetPattern("[%Y-%m-%d %H:%M:%S] [%l] [%n] [%t] [%F:%L] %v\n");
    }

    LogFormatter::LogFormatter(const std::string& pattern)
    {
        SetPattern(pattern);
    }

    LogFormatter::~LogFormatter() {}

    void LogFormatter::SetPattern(const std::string& pattern)
    {
#if __cplusplus >= 201703L
        std::unique_lock<std::shared_mutex> lock(mtx_);
#else
        std::unique_lock<std::mutex> lock(mtx_);
#endif
        if (!pattern_items_.empty())
            pattern_items_.clear();
        pattern_ = pattern;
        int idx = 0;
        while(idx < pattern_.size())
        {
            if (pattern_[idx] == '%')
            {
                idx++;
                if (pattern_[idx] == 'n')
                    pattern_items_.emplace_back(std::make_unique<LoggerNameItem>());
                else if (pattern_[idx] == 'l')
                    pattern_items_.emplace_back(std::make_unique<LogLevelItem>());
                else if (pattern_[idx] == 't')
                    pattern_items_.emplace_back(std::make_unique<ThreadIdItem>());
                else if (pattern_[idx] == 'v')
                    pattern_items_.emplace_back(std::make_unique<PayloadItem>());
                else if (pattern_[idx] == 'Y')
                    pattern_items_.emplace_back(std::make_unique<YearItem>());
                else if (pattern_[idx] == 'm')
                    pattern_items_.emplace_back(std::make_unique<MonthItem>());
                else if (pattern_[idx] == 'd')
                    pattern_items_.emplace_back(std::make_unique<DayItem>());
                else if (pattern_[idx] == 'H')
                    pattern_items_.emplace_back(std::make_unique<HourItem>());
                else if (pattern_[idx] == 'M')
                    pattern_items_.emplace_back(std::make_unique<MinuteItem>());
                else if (pattern_[idx] == 'S')
                    pattern_items_.emplace_back(std::make_unique<SecondItem>());
                else if (pattern_[idx] == 'F')
                    pattern_items_.emplace_back(std::make_unique<FileNameItem>());
                else if (pattern_[idx] == 'L')
                    pattern_items_.emplace_back(std::make_unique<LineNumberItem>());
                idx++;
            }
            else 
            {
                int begin = idx;
                while (idx < pattern_.size() && pattern_[idx] != '%')
                    idx++;
                // 此时idx要么指向%要么就在pattern_的末尾了
                pattern_items_.emplace_back(std::make_unique<OtherStringItem>(pattern_.substr(begin, idx - begin)));
            }
        }
    }

    std::string LogFormatter::Format(LogEvent::Ptr event) 
    {
#if __cplusplus >= 201703L
        std::shared_lock<std::shared_mutex> lock(mtx_);
#else
        std::unique_lock<std::mutex> lock(mtx_);
#endif 
        std::stringstream ss;
        for (auto& item : pattern_items_)
            item->Append(ss, event);
        return ss.str();
    }

} // namespace adalog